﻿using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using FluentNHibernate.Utils;
using NHibernate;
using NHibernate.Linq;
using NHibernate.Util;
using Remotion.Data.Linq;

namespace mForum.Infrastructure.NHibernate
{
    using mForum.Core.Common;
    using mForum.Domain.DomainObject;

    public class DatabaseContext : IDisposable
    {
        private readonly ISession _session;
        private ITransaction _transaction;

        private IQueryable<User> _users;

        public DatabaseContext(ISession session)
        {
            Check.Argument.IsNotNull(session, "session");

            _session = session;
        }

        public IQueryable<User> Users
        {
            [DebuggerStepThrough]
            get
            {
                return _users ?? (_users = CreateQuery<User>());
            }
        }

        public virtual IQueryable<TEntity> CreateQuery<TEntity>() where TEntity : class, IEntity
        {
            return _session.Query<TEntity>();
        }

        public virtual TEntity GetById<TEntity>(long id) where TEntity : class, IEntity
        {
            Check.Argument.IsNotNegative(id, "id");
            return _session.Get<TEntity>(id);
        }

        public virtual void Save<TEntity>(TEntity entity) where TEntity : class, IEntity
        {
            Check.Argument.IsNotNull(entity, "entity");
            EnsureTransaction();
            _session.SaveOrUpdate(entity);
        }

        public virtual void Delete<TEntity>(TEntity entity) where TEntity : class, IEntity
        {
            Check.Argument.IsNotNull(entity, "entity");
            EnsureTransaction();
            _session.Delete(entity);
        }

        public virtual void Commit()
        {
            EnsureTransaction();
            _transaction.Commit();
        }

        private void EnsureTransaction()
        {
            if (_transaction == null || !_transaction.IsActive || _transaction.WasCommitted || _transaction.WasRolledBack)
            {
                _transaction = _session.BeginTransaction();
            }
        }

        public void Dispose()
        {
            if (_transaction != null)
            {
                if (_transaction.IsActive)
                {
                    _transaction.Rollback();
                }

                _transaction.Dispose();
                _transaction = null;
            }

            if (_session != null)
            {
                if (_session.IsOpen)
                {
                    _session.Close();
                }

                _session.Dispose();
            }
        }
    }
}